//=========================================================================
// Copyright (C) 2024 The C++ Component Model(COMO) Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//=========================================================================

#ifndef __CONFIG_H__
#define __CONFIG_H__

#include <limits.h>
#include <time.h>
#include <netinet/in.h>
#include <re.h>

#ifdef __cplusplus
extern "C"
{
#endif

typedef int (*VOTER_OUTPUT_MSG)(char *s, size_t len, int outputTokenOrder,
                                                   uint64_t token, void *trans);

/**
 * 比 NooM 中的 M(最大参与表决的客户端数量) 要大，因为不是所有连接的客户端都处于活跃状态，比如
 * 可能是10个客户端，但它是一个三取二的功能安全架构，就意味道着只要有3个正常工作的节点就行，其它
 * 结果也可能是工作的，但不参与表决。
 */
#define MAX_CLIENTS     64

#define MIN_CLIENTS     3       // minimal requirement for voting，NooM 中的 N
#define MAX_NUM_VOTERS  32

/**
 * 用UDP协议发送时，用sendto函数最大能发送数据的长度为：
 * 65535- IP头(20) - UDP头(8)＝65507字节。用 sendto 函数发送数据时，如果发送数据长度大
 * 于该值，则函数会返回错误。
 * 由于IP有最大MTU，因此，
 * UDP 包的大小应该是 1500 - IP头(20) - UDP头(8) = 1472(Bytes)
 * TCP 包的大小应该是 1500 - IP头(20) - TCP头(20) = 1460 (Bytes)
 *
 * 用UDP传递表决数据, MAX_VoterMessage_SIZE 大于 65507 字节也是没有意义的。
 */
#define MAX_PROTO_STRING            256
#define MAX_VoterMessage_SIZE       256000
#define MAX_VoterMessage_BUF_SIZE   (MAX_VoterMessage_SIZE-sizeof(VoterMessage))
#define MAX_ENDPOINT_STR            256

#define MAX_NUM_TRANS               100                     // number of transactions

#define LINUX_NAMESPACE_LEN         32
#define SERVER_PORT                 7711
#define SERVER_PORT_UDP             7712
extern int g_iSERVER_PORT;
extern int g_iSERVER_PORT_UDP;
extern char g_iSERVER_NAMESPACE[LINUX_NAMESPACE_LEN];
extern char g_iSERVER_NAMESPACE_UDP[LINUX_NAMESPACE_LEN];

#define MAX_ARCH_SUPPORT            5
#define MAX_NETWORK_CARD            10
#define MAX_ip_prefix_LEN           20

#define MAX_ID_SIZE                 64
#define MAX_SIZE_NodeWhiteList      10
#define NODE_IN_USE                 1
#define NODE_NOT_IN_USE             0
typedef struct tagNodeWhiteList {
    char     ip[INET_ADDRSTRLEN];
    int      port;
    uint32_t in_use;
    char     id[MAX_ID_SIZE];
    char     src_ip[INET_ADDRSTRLEN];
    int      src_port;
} NodeWhiteList;
extern int g_numNodeWhiteList;
extern NodeWhiteList *g_pNodeWhiteList;

// output config
#define MAX_SIZE_OUTPUT_NodeList      100
extern int g_numOutputNodeList;
extern NodeWhiteList *g_pOutputNodeList;

// 表决周期                         // 654321
#define VOTING_CYCLE_NANOSECONDS    (1000000 * 200)      // 200毫秒

// 周期定时最大允许误差
#ifdef _IN_WSL_LINUX_
    #define MAX_DELAY_ERROR_NS      (1000 * 5000)        // 5000微秒
#else
    #define MAX_DELAY_ERROR_NS      (1000 * 500)         // 500微秒
#endif

// 客户端最大离线时间, 单位: 秒
#define MAX_CLIENT_IDLE_TIME  (3600 * 24 * 365)
                                                          // 123456
// Minimum Voting Interval, 最短投票间隔时间, 单位：微秒(1秒 = 1000000微秒)
// 单事务表决（即一个表决周期内只能表决一个事务）时
#define MINIMUM_VOTING_INTERVAL  (1000 * 200)
                                 //123

/**
 * “码距”是指两个数的二进制表示之间的汉明距离（Hamming Distance），即它们的二进制表示
 * 中不同位数的数量。为了找到64位整数中码距最大的数对，需要选择两个二进制表示差异最大的
 * 数，例如全1和全0的数。
 *
 * 64位整数，码距最大的10个数 (全1和全0交替)
 */
#define VOTER_TOKEN_0  0xFFFFFFFFFFFFFFFF  // 全1：18446744073709551615
#define VOTER_TOKEN_1  0x0000000000000000  // 全0：0
#define VOTER_TOKEN_2  0xAAAAAAAAAAAAAAAA  // 交替位：12297829382473034410
#define VOTER_TOKEN_3  0x5555555555555555  // 交替位：6148914691236517205
#define VOTER_TOKEN_4  0xFFFFFFFF00000000  // 前32位全1，后32位全0：18446744069414584320
#define VOTER_TOKEN_5  0x00000000FFFFFFFF  // 前32位全0，后32位全1：4294967295
#define VOTER_TOKEN_6  0xFFFF0000FFFF0000  // 每16位全1：18446462598732840960
#define VOTER_TOKEN_7  0x0000FFFF0000FFFF  // 每16位全0：281470681808895
#define VOTER_TOKEN_8  0xFF00FF00FF00FF00  // 每8位全1：18374966859414961920
#define VOTER_TOKEN_9  0x00FF00FF00FF00FF  // 每8位全0：71777214294589695

extern int g_voterOutputTokenOrder;
extern uint64_t g_i64bit_Tokens[];
extern int g_numVoterOutputToken;

typedef struct tagConfig {
    char    id[MAX_ID_SIZE];
    char    idVoter[MAX_ID_SIZE];

    char    sIP_port[PATH_MAX];
    char    sNodeIP_nodePort[PATH_MAX * 10];

    /**
     * 因为 表决数据到达的时间不完全同步，所以要表决 1 个事务，但如果是有 M 个客户端，
     * 那就可能要占用 M 个表决数据池 trans_arr 中的空间。
     */
    int32_t numTransPerVotingcycle;

    char    cDataSynchronizationProtocol;    // 'p': paxos, 'z': ZMQ

    int32_t N_in_NooM;
    /**
     * 实际参与表决的客户端数量，<=MAX_CLIENTS ， 因为有些客户端可能处于热备状态，可以参与
     * 表决，但稳定工作时，它不参与表决。
     */
    int32_t M_in_NooM;

    int32_t udp_data_in_raw_mode;

    int64_t nanoSecondsVOTING_CYCLE;
    int32_t maxWait;

    int64_t max_CLIENT_IDLE_TIME;

    int64_t minimum_VOTING_INTERVAL;
    int32_t minimum_VOTER_WORKING;
    int32_t minimum_VOTING_PASS;

    uint64_t dl_param_sched_runtime;
    uint64_t dl_param_sched_deadline;
    uint64_t dl_param_sched_period;

    VOTER_OUTPUT_MSG  voter_OUTPUT_MSG;
    int32_t  onlyMasterOutput;

    int32_t  numVoters;                 // 系统配置中表决器数，最大值：MAX_NUM_VOTERS

    int32_t  arch_total;
    char     nodeArches[MAX_ARCH_SUPPORT][MAX_ID_SIZE];
    regex_t  archPatterns[MAX_ARCH_SUPPORT][MAX_REGEXP_OBJECTS];
    unsigned char g_ccl_buf[MAX_ARCH_SUPPORT][MAX_CHAR_CLASS_LEN];

    char     cmdLineSetNetwork[MAX_NETWORK_CARD][MAX_ip_prefix_LEN];
    char     cmdLineDelNetwork[MAX_NETWORK_CARD][MAX_ip_prefix_LEN];
    int32_t  numCmdLineSetNetwork;
    int32_t  numcmdLineDelNetwork;

    int32_t  WeaklyPeriodic;    // Strongly(0) Weakly(1) Periodic
    int32_t  StrongOrder;       // 表决强有序，不可表决已经表决过的事务的前一个
} Config;
extern Config *g_pconfig;

int InNodeWhiteListCheck(char *client_ip, int client_port);

int ini_reader_handler(void *user, const char *section, const char *name,
                                                             const char *value);
void load_config_file_default();

char *strZcpy(char *dest, const char *src, size_t maxlen);

typedef struct tagMetadataWrapper {
    uint32_t crc_config;
    uint32_t crc_2;
} MetadataWrapper;
extern MetadataWrapper *g_pcomMetadata;

int TimespecToString(char *buffer, int bufferSize, struct timespec *ts);

// 下列 __DBEUG__ 宏用于打开不同调试信息的输出
#define __DBEUG__RT_delayTime   1

typedef enum tagVOTER_WORKING_MODE {         // 12345678
    DIALOG_CLIENT_SERVER                    = 0x10000000U,
    ONE_IN_ONE_OUT_WITH_SYNC                =     0x0001U,

    // 弱周期性，周期内的多个事务只是表决池，是为了表决事务对齐用的，一个表决周期内未完成的事务，将被定期清理
    MORE_IN_ONE_OUT_AT_ONCE_WITH_SYNC       =     0x0010U,
    MORE_IN_ONE_OUT_WITH_SYNC               =     0x0011U,
    MORE_IN_ONE_OUT_PERIODIC_WITH_SYNC      =     0x1000U,

    // 强周期性，一个表决周期内未表决成功的事务，将引起持别处理
    MULTI_IN_MULTI_OUT_PERIODIC_WITH_SYNC   = 0x11000001U,
    MULTI_IN_ONE_BY_ONE_OUT_PERIODIC        = 0x11000010U
} VOTER_WORKING_MODE;

typedef struct tagCFG_VOTER_WORKING_MODE {
    VOTER_WORKING_MODE  workingMode;
    char               *configStr;
} CFG_VOTER_WORKING_MODE;

extern VOTER_WORKING_MODE       g_Voter_Working_Mode;
extern CFG_VOTER_WORKING_MODE   g_cfg_Voter_Working_Mode[];

//#define VOTER__SUPPORT_ZMTP 1

/**
 * 可能作为 leveldb 的 key，这些关键字要与 InstanceID（8 字节整数编码） 区分开，所以长度
 * 必须大于8字节
 *                                                 123456789
 * _Voter_#{voterId}#{idTransaction}） | at least: _Voter_##, 9 characters
 * VResult#{voterId}#{idTransaction}   | at least: VResult##, 9 characters
 */
#define CONCAT(a, b) a##b
#define STRINGIFY(x) #x

#define MACRO_VOTERID_FMTSTR(x) "_Voter_#%" STRINGIFY(x) "[^#]#%" PRIu64
#define MACRO_VOTERID_PRINTF_FMTSTR "_Voter_#%s#%" PRIu64
#define MACRO_VOTERID_PRINTF_FMTSTR_2 "_Voter_#%s.%s#%" PRIu64

#define MACRO_VR_FMTSTR(x) "VResult#%" STRINGIFY(x) "[^#]#%" PRIu64
#define MACRO_VR_PRINTF_FMTSTR "VResult#%s#%" PRIu64


#ifdef __cplusplus
}
#endif

#endif // __CONFIG_H__
